package com.cv.gateway.filter.security;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.cv.gateway.util.SecurityFrameworkUtil;
import com.cv.system.api.token.TokenApi;
import com.cv.system.api.token.form.LoginUser;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * Token 过滤器，验证 token 的有效性
 * 1. 验证通过时，将 userId、userType 通过 Header 转发给服务
 * 2. 验证不通过，还是会转发给服务。因为，接口是否需要登录的校验，还是交给服务自身处理
 *
 * @author Charles_XDXD
 */
@Component
public class TokenAuthenticationFilter implements GlobalFilter, Ordered {

    @DubboReference
    private TokenApi tokenApi;

    /**
     * 空的 LoginUser 的结果
     */
    private static final LoginUser LOGIN_USER_EMPTY = new LoginUser();

    @Override
    public Mono<Void> filter(final ServerWebExchange exchange, GatewayFilterChain chain) {
        // 移除 login-user 的请求头，避免伪造模拟
        SecurityFrameworkUtil.removeLoginUser(exchange);

        // 如果没有 Token 令牌，则直接继续 filter
        String token = SecurityFrameworkUtil.obtainAuthorization(exchange);
        if (StrUtil.isEmpty(token)) {
            return chain.filter(exchange);
        }

        // 如果有 Token 令牌，则解析对应 userId、userType 等字段，并通过 通过 Header 转发给服务
        LoginUser user = getLoginUser(token);

        // 1. 无用户，直接 filter 继续请求
        if (user == LOGIN_USER_EMPTY) {
            return chain.filter(exchange);
        }

        // 2.1 有用户，则设置登录用户
        SecurityFrameworkUtil.setLoginUser(exchange, user);
        // 2.2 将 user 并设置到 login-user 的请求头，使用 json 存储值
        ServerWebExchange newExchange = exchange.mutate()
                .request(builder -> SecurityFrameworkUtil.setLoginUserHeader(builder, user)).build();
        return chain.filter(newExchange);
    }

    private LoginUser getLoginUser(String token) {

        if (!tokenApi.validateToken(token)) {
            return LOGIN_USER_EMPTY;
        }

        LoginUser remoteUser = tokenApi.getLoginUser(token);
        if (ObjUtil.isNotEmpty(remoteUser)) {
            return remoteUser;
        }

        return LOGIN_USER_EMPTY;
    }

    @Override
    public int getOrder() {
        return -100; // 和 Spring Security Filter 的顺序对齐
    }

}
